﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Globalization;
using System.Linq;
using BookShop.DAL;
using BookShop.DTO;
using BookShop.Library.Constant;

namespace BookShop.BLL
{
    public class BookService
    {
        public static Book Add(Book book)
        {
            using (var repository = new BookRepository())
            {
                return repository.Add(book);
            }
        }

        public static bool CheckIsNew(long bookId)
        {
            var book = GetById(bookId);
            if (book == null)
            {
                return false;
            }
            return book.CreateTime >= DateTime.Now.AddDays(-7);
        }

        public static bool CheckIsOnSale(long bookId)
        {
            var book = GetById(bookId);
            if (book == null)
            {
                return false;
            }
            var discountBookId = DiscountService.GetAllEffectiveSale().GroupBy(g => g.BookId).Select(g => g.Key);
            return discountBookId.Contains(book.Id);
        }

        public static bool Delete(long bookId)
        {
            using (var repository = new BookRepository())
            {
                var book = GetById(bookId);
                if (book == null)
                {
                    return false;
                }
                book.Status = BookStatus.Inactive;
                book = repository.Update(book);
                return book != null;
            }
        }

        public static List<Book> GetAll()
        {
            using (var repository = new BookRepository())
            {
                return repository.FindAll(b => b.Status == BookStatus.Active).OrderBy(b => b.Title).ToList();
            }
        }

        public static double GetAverageRatePoint(long bookId)
        {
            var book = GetById(bookId);
            return book != null ? RateCommentService.GetByBookId(bookId).Average(r => r.RatePoint) : 0;
        }

        public static List<Book> GetBestSellers(int? count, int bestSellerPeriod)
        {
            var bookRepository = new BookRepository();
            var orderRepository = new OrderRepository();
            var orderDetailRepository = new OrderDetailRepository();
            var orderList = orderRepository.FindAll(o => o.CreateTime.Year == DateTime.Now.Year).ToList();
            if (bestSellerPeriod == TimePeriod.Month)
            {
                orderList = orderList.FindAll(o => o.CreateTime.Month == DateTime.Now.Month);
            }
            var orderIdList = orderList.Select(o => o.Id);
            var orderDetailList = orderDetailRepository.FindAll(o => orderIdList.Contains(o.OrderId)).ToList();
            var bookIdList =
                orderDetailList.GroupBy(o => o.BookId)
                    .Select(g => new { BookId = g.Key, DetailCount = g.Count() })
                    .OrderByDescending(g => g.DetailCount)
                    .Select(g => g.BookId)
                    .ToList();
            var returnList = bookRepository.FindAll(b => bookIdList.Contains(b.Id) && b.Status == BookStatus.Active);
            if (count.HasValue)
            {
                returnList = returnList.Take(count.Value);
            }
            return returnList.ToList();
        }

        public static List<Book> GetByAuthorId(int authorId)
        {
            using (var repository = new BookRepository())
            {
                var returnList = new List<Book>();
                var author = AuthorService.GetById(authorId);
                if (author != null)
                {
                    returnList = repository.FindAll(b => b.AuthorId == authorId && b.Status == BookStatus.Active).ToList();
                }
                return returnList;
            }
        }

        public static List<Book> GetByCategoryId(int categoryId)
        {
            using (var repository = new BookRepository())
            {
                return repository.FindAll(b => b.CategoryId == categoryId && b.Status == BookStatus.Active).ToList();
            }
        }

        public static Book GetById(long bookId)
        {
            using (var repository = new BookRepository())
            {
                return repository.Find(b => b.Id == bookId);
            }
        }

        public static List<Book> GetByPublisherId(int publisherId)
        {
            using (var repository = new BookRepository())
            {
                return repository.FindAll(b => b.PublisherId == publisherId && b.Status == BookStatus.Active).ToList();
            }
        }

        public static List<Book> GetDiscount(int? count)
        {
            using (var repository = new BookRepository())
            {
                var discountRepository = new DiscountRepository();
                var discountBookIdList =
                    discountRepository.FindAll(d => d.EndDate >= DateTime.Now)
                        .OrderByDescending(d => d.Percentage)
                        .Select(d => d.BookId);
                var returnList =
                    repository.FindAll(b => discountBookIdList.Contains(b.Id) && b.Status == BookStatus.Active).ToList();
                if (count.HasValue)
                {
                    returnList = returnList.Take(count.Value).ToList();
                }
                return returnList;
            }
        }

        public static double GetDiscountPrice(long bookId)
        {
            var book = GetById(bookId);
            if (book == null)
            {
                return 0;
            }
            var discount = DiscountService.GetAllEffectiveSale().Find(d => d.BookId == bookId);
            if (discount == null)
            {
                return book.Price;
            }
            return book.Price - book.Price * discount.Percentage / 100;
        }

        public static List<Book> GetNewest(int? count)
        {
            using (var repository = new BookRepository())
            {
                var timeLine = DateTime.Now.AddDays(-7);
                var returnList =
                    repository.FindAll(b => b.Status == BookStatus.Active && b.CreateTime >= timeLine)
                        .OrderByDescending(b => b.CreateTime)
                        .ToList();
                if (count.HasValue)
                {
                    returnList = returnList.Take(count.Value).ToList();
                }
                return returnList;
            }
        }

        public static List<Book> GetTopRated(int? count)
        {
            using (var repository = new BookRepository())
            {
                var returnList = repository.FindAll(b => b.Status == BookStatus.Active)
                        .OrderByDescending(b => b.AverageRatePoint)
                        .ToList();
                if (count.HasValue)
                {
                    returnList = returnList.Take(count.Value).ToList();
                }
                return returnList;
            }
        }

        public static List<Book> Search(long? bookId, string title, int? categoryId, int? authorId, int? publisherId,
            string fromPrice, string toPrice, int? status, int page, int pageSize, out int count)
        {
            var bookRepository = new BookRepository();
            var returnList = new List<Book>();
            if (bookId.HasValue)
            {
                var book = GetById(bookId.Value);
                returnList.Add(book);
            }
            else
            {
                returnList = bookRepository.GetAll().ToList();
                if (!string.IsNullOrWhiteSpace(title))
                {
                    returnList = returnList.FindAll(b => b.Title.ToLower().Contains(title.Trim().ToLower()));
                }
                if (categoryId.HasValue)
                {
                    returnList = returnList.FindAll(b => b.CategoryId == categoryId.Value);
                }
                if (authorId.HasValue)
                {
                    returnList = returnList.FindAll(b => b.AuthorId == authorId.Value);
                }
                if (publisherId.HasValue)
                {
                    returnList = returnList.FindAll(b => b.PublisherId == publisherId.Value);
                }
                double newFromPrice;
                var isValidFromPrice = double.TryParse(fromPrice, NumberStyles.AllowDecimalPoint,
                    CultureInfo.CreateSpecificCulture("en-US"), out newFromPrice);
                double newToPrice;
                var isValidToPrice = double.TryParse(toPrice, NumberStyles.AllowDecimalPoint,
                    CultureInfo.CreateSpecificCulture("en-US"), out newToPrice);
                if (isValidFromPrice && isValidToPrice)
                {
                    if (newFromPrice >= 0 && newToPrice >= 0 && newFromPrice <= newToPrice)
                    {
                        returnList = returnList.FindAll(b => b.Price >= newFromPrice && b.Price <= newToPrice);
                    }
                }
                if (isValidFromPrice && !isValidToPrice)
                {
                    if (newFromPrice > 0)
                    {
                        returnList = returnList.FindAll(b => b.Price >= newFromPrice);
                    }
                }
                if (!isValidFromPrice && isValidToPrice)
                {
                    if (newToPrice > 0)
                    {
                        returnList = returnList.FindAll(b => b.Price <= newToPrice);
                    }
                }
                if (status.HasValue)
                {
                    returnList = returnList.FindAll(b => b.Status == status.Value);
                }
            }
            count = returnList.Count;
            returnList = returnList.Skip((page - 1) * pageSize).Take(pageSize).ToList();
            foreach (var book in returnList)
            {
                book.AuthorName = (AuthorService.GetById(book.AuthorId) ?? new Author()).Name;
                book.CategoryName = (CategoryService.GetById(book.CategoryId) ?? new Category()).Name;
                book.PublisherName = (PublisherService.GetById(book.PublisherId) ?? new Publisher()).Name;
            }
            return returnList;
        }

        public static Book Update(Book book)
        {
            using (var repository = new BookRepository())
            {
                return repository.Update(book);
            }
        }

        public static Book UpdateAverageRatePoint(long bookId)
        {
            var book = GetById(bookId);
            if (book != null)
            {
                book.AverageRatePoint = RateCommentService.GetByBookId(bookId).Average(r => r.RatePoint);
                book = Update(book);
            }
            return book;
        }

        public static List<Book> UpdateNotMappedField(List<Book> listBook)
        {
            var returnList = new List<Book>();
            if (listBook != null)
            {
                foreach (var book in listBook)
                {
                    returnList.Add(UpdateNotMappedField(book));
                }
            }
            return returnList;
        }

        public static Book UpdateNotMappedField(Book book)
        {
            if (book != null)
            {
                book.AuthorName = (AuthorService.GetById(book.AuthorId) ?? new Author()).Name;
                book.CategoryName = (CategoryService.GetById(book.CategoryId) ?? new Category()).Name;
                book.PublisherName = (PublisherService.GetById(book.PublisherId) ?? new Publisher()).Name;
                book.IsNew = CheckIsNew(book.Id);
                book.IsOnSale = CheckIsOnSale(book.Id);
                book.InStock = book.Quantity > 0;
                book.RateComments = RateCommentService.GetByBookId(book.Id);
                book.DiscountPrice = GetDiscountPrice(book.Id);
                foreach (var rateComment in book.RateComments)
                {
                    var customer = CustomerService.GetById(rateComment.CustomerId);
                    rateComment.CustomerName = customer != null ? customer.FullName : string.Empty;
                    rateComment.BookTitle = book.Title;
                }
            }
            return book;
        }
    }
}
